#include "../pch.h"
#include "REFEncoding.h"
#include "REFASCIIEncoding.h"
#include "REFDecoderReplacementFallback.h"

namespace BFX
{

REFASCIIEncoding::REFASCIIEncoding() :
	REFEncoding(REFEncoding::CodePageASCII)
{
}

REFASCIIEncoding::~REFASCIIEncoding()
{
}

int REFASCIIEncoding::GetMaxByteCount(int nCharCount) const
{
	BFX_REQUIRE1 (nCharCount >= 0, "Non-negative number required.");

	// Characters would be # of characters + 1 in case high surrogate is ? * max fallback 
	long long lByteCount = (long long)nCharCount + 1;

	BFX_ENSURE1 (lByteCount <= 0x7fffffff, "Too many characters. The resulting number of bytes is larger than what can be returned as an int.");
	return (int)lByteCount;
}

int REFASCIIEncoding::GetMaxCharCount(int nByteCount) const
{
	BFX_REQUIRE1 (nByteCount >= 0, "Non-negative number required.");

	// Just return length, SBCS stay the same length because they don't map to surrogate
	long long lCharCount = (long long)nByteCount;

	BFX_ENSURE1 (lCharCount <= 0x7fffffff, "Too many bytes. The resulting number of chars is larger than what can be returned as an int.");

	return (int)lCharCount;
}

// GetByteCount 
// Note: We start by assuming that the output will be the same as count.  Having
// an encoder or fallback may change that assumption 
int REFASCIIEncoding::GetByteCount(const UCHAR* pChars, int nCharCount) const
{
	// Just need to BFX_ASSERT, this is called by something else internal that checked parameters already
	BFX_ASSERT(nCharCount >= 0);
	BFX_ASSERT(pChars != NULL);

	// Start by assuming default count, then +/- for fallback characters 
	const UCHAR* pCharEnd = pChars + nCharCount;

	bool bFallingBack = false;
	// TODO: faster version

	// Count is more complicated if you have a funky fallback
	// For fallback we may need a fallback buffer, we know we're not default fallback 
	int nByteCount = 0;

	// Now we may have fallback char[] already from the encoder 

	// Go ahead and do it, including the fallback.
	UCHAR ch;
	while ((ch = bFallingBack ? '?' : '\0') != 0 || pChars < pCharEnd)
	{
		bFallingBack = false;

		// First unwind any fallback
		if (ch == 0) 
		{
			// No fallback, just get next char
			ch = *pChars;
			pChars++;
		}

		// Check for fallback, this'll catch surrogate pairs too. 
		// no chars >= 0x80 are allowed.
		if (ch > 0x7f) 
		{

			bFallingBack = true;
			pChars ++;
			continue;
		} 

		// We'll use this one
		nByteCount++; 
	}

	return nByteCount;
}

int REFASCIIEncoding::GetBytes(const UCHAR* pChars, int nCharCount, OUT BYTE* pBytes, int nByteCount) const
{
	// Just need to BFX_ASSERT, this is called by something else internal that checked parameters already
	BFX_ASSERT(pBytes != NULL); 
	BFX_ASSERT(nByteCount >= 0);
	BFX_ASSERT(pChars != NULL);
	BFX_ASSERT(nCharCount >= 0);

	bool bFallingBack = false;

	// prepare our end
	const UCHAR* pCharEnd = pChars + nCharCount;
	BYTE* pByteStart = pBytes;
	const UCHAR* charStart = pChars;

	// See if we do the fast default or slightly slower fallback 
	// TODO: faster version

	// Slower version, have to do real fallback.

	// prepare our end
	BYTE* pByteEnd = pBytes + nByteCount;

	// Now we may have fallback char[] already from the encoder

	// Go ahead and do it, including the fallback.
	UCHAR ch; 
	while ((ch = bFallingBack ? '?' : '\0') != 0 || pChars < pCharEnd)
	{
		bFallingBack = false;

		// First unwind any fallback
		if (ch == 0) 
		{
			// No fallback, just get next char
			ch = *pChars;
			pChars++; 
		}

		// Check for fallback, this'll catch surrogate pairs too. 
		// All characters >= 0x80 must fall back.
		if (ch > 0x7f) 
		{
			// Get Fallback
			bFallingBack = true;
			pChars ++;

			// Go ahead & continue (& do the fallback) 
			continue; 
		}

		// We'll use this one
		// Bounds check
		if (pBytes >= pByteEnd)
		{
			BFX_ASSERT(!bFallingBack);

			// didn't use this char, we'll throw or use buffer
			BFX_ASSERT(pChars > charStart || pBytes == pByteStart);	// Expected chars to have advanced already.
			pChars--;											// don't use last char

			// Are we throwing or using buffer? 
			BFX_ENSURE(pBytes != pByteStart);
			break;                                              // don't throw, stop
		} 

		// Go ahead and add it
		*pBytes = (BYTE)ch;
		pBytes++; 
	}

	return (int)(pBytes - pByteStart);
}

int REFASCIIEncoding::GetCharCount(const BYTE* pBytes, int nCount, REFDecoder* pDecoder) const
{
	// Just assert, we're called internally so these should be safe, checked already
	BFX_ASSERT(pBytes != NULL);
	BFX_ASSERT(nCount >= 0);

	if (GetDecoderFallback()->GetMaxCharCount() == 1)
	{
		// Just return length, SBCS stay the same length because they don't map to surrogate
		// pairs and we don't have a decoder fallback.
		return nCount;
	}

	// Only need decoder fallback buffer if not using default replacement fallback.
	REF<REFDecoderFallbackBuffer> pFallbackBuffer;

	// Have to do it the hard way.
	// Assume nCharCount will be == count 
	int nCharCount = nCount;

	// Do it our fast way
	const BYTE* pByteEnd = pBytes + nCount; 

	// Quick loop
	while (pBytes < pByteEnd)
	{ 
		// Faster if don't use *bytes++;
		BYTE b = *pBytes; 
		pBytes++; 

		// If unknown we have to do fallback count 
		if (b >= 0x80)
		{
			if (pFallbackBuffer == NULL)
			{
				if (pDecoder == NULL)
					pFallbackBuffer = GetDecoderFallback()->CreateFallbackBuffer();
				else
					pFallbackBuffer = pDecoder->GetFallbackBuffer();
				pFallbackBuffer->Initialize(pByteEnd - nCount, NULL);
			}
			// Use fallback buffer
			SequenceT<BYTE> bytesUnknown;
			bytesUnknown.Append(b);
			nCharCount --;            // Have to unreserve the one we already allocated for b
			nCharCount += pFallbackBuffer->Fallback(bytesUnknown, pBytes);
		} 
	}

	// Fallback buffer must be empty
	BFX_ASSERT1(pFallbackBuffer == NULL || pFallbackBuffer->GetRemaining() == 0, "Expected Empty fallback buffer");

	// Converted sequence is same length as input
	return nCharCount;
}

int REFASCIIEncoding::GetChars(const BYTE* pBytes, int nByteCount, OUT UCHAR* pChars, int nCharCount, REFDecoder* pDecoder) const
{
	// Just need to BFX_ASSERT, this is called by something else internal that checked parameters already
	BFX_ASSERT(pBytes != NULL); 
	BFX_ASSERT(nByteCount >= 0);
	BFX_ASSERT(pChars != NULL);
	BFX_ASSERT(nCharCount >= 0);

	// Do it fast way if using ? replacement fallback 
	const BYTE* pByteEnd = pBytes + nByteCount;
	const BYTE* pByteStart = pBytes;
	UCHAR* pCharStart = pChars;

	if (GetDecoderFallback()->GetMaxCharCount() == 1)
	{
		// Try it the fast way
		// NOTE: Should never do this!!!
		UCHAR replacementChar = ((REFDecoderReplacementFallback*)GetDecoderFallback())->GetReplacementChar();

		// Need byteCount chars, otherwise too small buffer
		if (nCharCount < nByteCount)
		{
			// Need at least 1 output byte, throw if must throw
			BFX_REQUIRE(nCharCount >- 1);

			// Not throwing, use what we can
			pByteEnd = pBytes + nCharCount;
		}

		// Quick loop, just do '?' replacement because we don't have fallbacks for decodings.
		while (pBytes < pByteEnd)
		{
			BYTE b = *(pBytes++);
			if (b >= 0x80)
				// This is an invalid byte in the ASCII encoding.
				*(pChars++) = replacementChar;
			else
				*(pChars++) = (UCHAR)b;
		}

		// bytes & chars used are the same
		// TODO: if (pDecoder != NULL)
		// TODO:	 pDecoder->m_nBytesUsed = (int)(pBytes - pByteStart);
		return (int)(pChars - pCharStart);
	}

	// Slower way's going to need a fallback buffer
	REF<REFDecoderFallbackBuffer> pFallbackBuffer = NULL;
	UCHAR*   pCharEnd = pChars + nCharCount;

	// Not quite so fast loop
	while (pBytes < pByteEnd) 
	{
		// Faster if don't use *bytes++; 
		BYTE b = *(pBytes); 
		pBytes++;

		if (b >= 0x80)
		{
			// This is an invalid byte in the ASCII encoding.
			if (pFallbackBuffer == NULL)
			{
				if (pDecoder == NULL)
					pFallbackBuffer = GetDecoderFallback()->CreateFallbackBuffer();
				else
					pFallbackBuffer = pDecoder->GetFallbackBuffer();
				pFallbackBuffer->Initialize(pByteEnd - nByteCount, pCharEnd);
			}

			// Use fallback
			SequenceT<BYTE> bytesUnknown;
			bytesUnknown.Append(b);

			// Note that chars won't get updated unless this succeeds
			if (!pFallbackBuffer->Fallback(bytesUnknown, pBytes, INOUT pChars))
			{
				// May or may not throw, but we didn't get this byte
				BFX_ASSERT1(pBytes > pByteStart || pChars == pCharStart, "Expected bytes to have advanced already (fallback case)");
				pBytes --;                                          // unused byte
				pFallbackBuffer->ResetAll();                        // Didn't fall this back
				BFX_ENSURE(pChars != pCharStart);					// throw?
				break;                                              // don't throw, but stop loop
			}
		} 
		else
		{
			// Make sure we have buffer space
			if (pChars >= pCharEnd) 
			{
				BFX_ASSERT(pBytes > pByteStart || pChars == pCharStart);	// Expected bytes to have advanced already (normal case)
				pBytes--;                                           // unused byte

				BFX_ENSURE(pChars != pCharStart);					// throw? 
				break;                                              // don't throw, but stop loop
			}

			*(pChars) = (UCHAR)b;
			pChars++;
		} 
	} 

	// Might have had decoder fallback stuff.
	// TOD: if (pDecoder != NULL)
	// TOD: 	pDecoder->m_nBytesUsed = (int)(pBytes - pByteStart);

	// Expect Empty fallback buffer for GetChars
	BFX_ASSERT1(pFallbackBuffer == NULL || pFallbackBuffer->GetRemaining() == 0, "Expected Empty fallback buffer");

	return (int)(pChars - pCharStart);
}

}	//	namespace BFX
